home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 #1 / Ham Radio 2000.iso / ham2000 / tcp_ip / wnos / wn941101 / icmpcmd.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-08-10  |  15.1 KB  |  512 lines

  1. /* ICMP-related user commands */
  2. #include <stdio.h>
  3.  
  4. #include "global.h"
  5. #include "icmp.h"
  6. #include "ip.h"
  7. #include "mbuf.h"
  8. #include "netuser.h"
  9. #include "internet.h"
  10. #include "timer.h"
  11. #include "socket.h"
  12. #include "proc.h"
  13. #include "session.h"
  14. #include "cmdparse.h"
  15. #include "commands.h"
  16. #include "tcp.h"         /* used for rtt_add() */
  17.  
  18. static void pingtx __ARGS((int s,void *ping1,void *p));
  19. static void _setping(void *x);
  20.  
  21. int Icmp_trace = 0;
  22. static int Icmp_echo = 1;
  23.  
  24. static int
  25. doicmpec(argc,argv,p)
  26. int argc;
  27. char *argv[];
  28. void *p;
  29. {
  30.    return setbool(&Icmp_echo,"ICMP echo response accept",argc,argv);
  31. }
  32.  
  33. static int
  34. doicmpstat(argc,argv,p)
  35. int argc;
  36. char *argv[];
  37. void *p;
  38. {
  39.     int i, j;
  40.  
  41.     /* Note that the ICMP variables are shown in column order, because
  42.      * that lines up the In and Out variables on the same line
  43.      */
  44.     for(j = i = 1; i <= NUMICMPMIB; i++) {
  45.         tprintf("(%2u)icmp%-16s%10lu",
  46.             i,Icmp_mib[i].name,Icmp_mib[i].value.integer);
  47.         tputs((j++ % 2) ? "     " : "\n");
  48.     }
  49.     if((j % 2) == 0)
  50.       tputs("\n");
  51.  
  52.     return 0;
  53. }
  54.  
  55. static int
  56. doicmptr(argc,argv,p)
  57. int argc;
  58. char *argv[];
  59. void *p;
  60. {
  61.    return setbool(&Icmp_trace,"ICMP trace",argc,argv);
  62. }
  63.  
  64. int
  65. doicmp(argc,argv,p)
  66. int argc;
  67. char *argv[];
  68. void *p;
  69. {
  70.     struct cmds Icmpcmds[] = {
  71.         "echo",         doicmpec,       0, 0, NULLCHAR,
  72.         "status",       doicmpstat,     0, 0, NULLCHAR,
  73.         "trace",        doicmptr,       0, 0, NULLCHAR,
  74.         NULLCHAR
  75.     };
  76.  
  77.    return subcmd(Icmpcmds,argc,argv,p);
  78. }
  79.  
  80. /* Send ICMP Echo Request packets */
  81. int
  82. doping(argc,argv,p)
  83. int argc;
  84. char *argv[];
  85. void *p;
  86. {
  87. struct proc *pinger = NULLPROC; /* Transmit process */
  88. struct sockaddr_in from;
  89. struct icmp icmp;
  90. struct mbuf *bp;
  91. int32 timestamp, rtt;
  92. int s, fromlen;
  93. struct ping ping;
  94. struct session *sp;
  95.  
  96.    memset((char *)&ping,0,sizeof(ping));
  97.  
  98.    if((ping.target = resolve(argv[1])) == 0) {
  99.       tprintf(Badhost,argv[1]);
  100.       return 1;
  101.    }
  102.    /* Allocate a session descriptor */
  103.    if((sp = ping.sp = newsession(argv[1],PING,0,0)) == NULLSESSION){
  104.       tputs(Nosess);
  105.       return 1;
  106.    }
  107.    if((sp->s = s = socket(AF_INET,SOCK_RAW,IPPROTO_ICMP)) == -1){
  108.       tputs(Nosocket);
  109.       keywait(NULLCHAR,1);
  110.       freesession(sp);
  111.       return 1;
  112.    }
  113.  
  114.    if(argc > 2)
  115.       ping.len = (int16)min(atoi(argv[2]),1000);
  116.  
  117.    if(argc > 3)
  118.       ping.interval = atol(argv[3]) * 1000L;
  119.  
  120. #ifdef XXX
  121.    /* This is a hack. If the specified interval is less than one tick,
  122.    * assume it to be in seconds. Otherwise assume milliseconds.
  123.    */
  124.    if(ping.interval < MSPTICK)
  125.       ping.interval *= (1000 / MSPTICK);
  126.    else
  127.       ping.interval /= MSPTICK;
  128. #endif
  129.  
  130.    /* Optionally ping a range of IP addresses */
  131.    if(argc > 4)
  132.       ping.incflag = 1;
  133.  
  134.    if(ping.interval != 0){
  135.       pinger = newproc("pingtx",1024,pingtx,s,&ping,NULL,0);
  136.    } else {
  137.       /* One shot ping; let echo_proc hook handle response.
  138.       * An ID of MAXINT16 will not be confused with a legal socket
  139.       * number, which is used to identify repeated pings
  140.       */
  141.       pingem(s,ping.target,0,MAXINT16,ping.len);
  142.       freesession(sp);
  143.       return 0;
  144.    }
  145.    /* Now collect the replies */
  146.    for(;;){
  147.       fromlen = sizeof(from);
  148.       if(recv_mbuf(s,&bp,0,(char *)&from,&fromlen) == -1)
  149.          break;
  150.       ntohicmp(&icmp,&bp);
  151.       if(icmp.type != ICMP_ECHO_REPLY || icmp.args.echo.id != s){
  152.          /* Ignore other people's responses */
  153.          free_p(bp);
  154.          continue;
  155.       }
  156.       ping.responses++;
  157.       /* Get stamp */
  158.       if(pullup(&bp,(char *)×tamp,sizeof(timestamp)) != sizeof(timestamp)){
  159.          /* The timestamp is missing! */
  160.          free_p(bp);     /* Probably not necessary */
  161.          continue;
  162.       }
  163.       free_p(bp);
  164.  
  165.      /* Compute round trip time, update smoothed estimates */
  166.      rtt = msclock() - timestamp;
  167.      if(ping.incflag){
  168.         tprintf("%s: rtt %lu\n",inet_ntoa(from.sin_addr.s_addr), rtt);
  169.         continue;
  170.      }
  171.      if(ping.responses == 1){
  172.         /* First response, base entire SRTT on it */
  173.         ping.srtt = rtt;
  174.         ping.mdev = 0;
  175.      } else {
  176.         int32 abserr = (rtt > ping.srtt) ? (rtt-ping.srtt) : (ping.srtt-rtt);
  177.         ping.srtt = (7*ping.srtt + rtt + 4) >> 3;
  178.         ping.mdev = (3*ping.mdev + abserr + 2) >> 2;
  179.      }
  180.    }
  181.    if(pinger != NULLPROC)
  182.      killproc(pinger);
  183.    freesession(sp);
  184.    return 0;
  185. }
  186.  
  187. void
  188. echo_proc(int32 source,int32 dest,struct icmp *icmp,struct mbuf *bp)
  189. {
  190. int32 timestamp;
  191.  
  192.     if(Icmp_echo && icmp->args.echo.id == MAXINT16
  193.       && pullup(&bp,(char *)×tamp,sizeof(timestamp)) == sizeof(timestamp)) {
  194.         /* Compute round trip time */
  195.         int32 rtt = msclock() - timestamp;
  196.  
  197.         tprintf("%s: rtt %lu\n",inet_ntoa(source),rtt);
  198.         rtt_add(source,(rtt*3)/2);
  199.     }
  200.     free_p(bp);
  201. }
  202.  
  203. /* Ping transmit process. Runs until killed */
  204. static void
  205. pingtx(int s,void *ping1,void *p)
  206. {
  207. struct ping *ping = (struct ping *)ping1;
  208.  
  209.    ping->sent = 0;
  210.  
  211.    if(ping->incflag){
  212.       for(;;){
  213.          tprintf("pinging %s...\n",inet_ntoa(ping->target));
  214.          pingem(s,ping->target++,0,(int16)s,ping->len);
  215.          pause(ping->interval);
  216.       }
  217.    } else {
  218.       tprintf("pinging %s...\n",ping->sp->name);
  219.       for(;;){
  220.         if(ping->sent) {
  221.           tprintf("sent%6lu  rcvd%6lu  %% %3lu  avg rtt%6lu  mdev%5lu\n",
  222.             ping->sent,ping->responses,
  223.             (ping->responses*100 + ping->sent/2)/ping->sent,
  224.             ping->srtt,ping->mdev);
  225.         }
  226.         pingem(s,ping->target,(int16)ping->sent++,(int16)s,ping->len);
  227.         pause(ping->interval);
  228.       }
  229.    }
  230. }
  231.  
  232.  
  233. /*----------------------------------------------------------------------*
  234. * Send ICMP Echo Request packet                                         *
  235. *-----------------------------------------------------------------------*/
  236. static int
  237. pingem(
  238. int s,                              /* Raw socket on which to send ping */
  239. int32 target,                       /* Site to be pinged */
  240. int16 seq,                          /* ICMP Echo Request sequence number */
  241. int16 id,                           /* ICMP Echo Request ID */
  242. int16 len)                          /* Length of optional data field */
  243. {
  244. struct mbuf *bp;
  245. struct icmp icmp;
  246. struct sockaddr_in to;
  247.  
  248.    int32 clock = msclock();
  249.    int x = (len+sizeof(clock));
  250.    struct mbuf *data = ambufw((int16)(x));
  251.  
  252.    data->cnt = len+sizeof(clock);
  253.  
  254.    /* Set optional data field, if any, to all 55's */
  255.    if(len != 0)
  256.       memset(data->data+sizeof(clock),0x55,len);
  257.  
  258.    /* Insert timestamp and build ICMP header */
  259.    memcpy(data->data,(char *)&clock,sizeof(clock));
  260.    icmpOutEchos++;
  261.    icmpOutMsgs++;
  262.    icmp.type = ICMP_ECHO;
  263.    icmp.code = 0;
  264.    icmp.args.echo.seq = seq;
  265.    icmp.args.echo.id = id;
  266.    if((bp = htonicmp(&icmp,data)) == NULLBUF){
  267.       free_p(data);
  268.       return 0;
  269.    }
  270.    to.sin_family = AF_INET;
  271.    to.sin_addr.s_addr = target;
  272.    send_mbuf(s,bp,0,(char *)&to,sizeof(to));
  273.    return 0;
  274. }
  275.  
  276. /*----------------------------------------------------------------------*
  277. *  UNDER CONSTRUCTION !!!!                                              *
  278. * provide a timer triggered ping to signal our presece to RPSF Servers  *
  279. * Syntax:                                                               *
  280. *         setping <address> <interval>                                  *
  281. *-----------------------------------------------------------------------*/
  282.  
  283. typedef enum   {
  284.    Bad, Suspect, Good
  285. } Apstate;
  286.  
  287. static char *apstring[3] =   {
  288.    "Bad","Suspect","Good"
  289. };
  290.  
  291. typedef struct aping  {
  292.    struct aping *next;               /* next in list                    */
  293.    int32 target;                     /* target to be pinged             */
  294.    Apstate state;                    /* state Bad,Suspect,Good          */
  295.    Boolean proc_run;                 /* True if pocess running          */
  296.    struct timer timer;               /* connected timer                 */
  297. } Aping;
  298.  
  299. static Aping *Ap;                    /* start of list                   */
  300.  
  301.  
  302. /*----------------------------------------------------------------------*
  303. *-----------------------------------------------------------------------*/
  304. int
  305. dosetping(int argc,char *argv[], void *p)
  306. {
  307. int32 target, timeout;
  308. Aping *ap=0, *app;
  309.  
  310.    if(argc < 2) {
  311.       /*----------------------------------------------------------------*
  312.       * display the Pinglist                                            *
  313.       *-----------------------------------------------------------------*/
  314.       tputs("Status   Address            Interval\n");
  315.       for (app = Ap; app; app = app->next)   {
  316.          tprintf("%-8s %-18s %lu/%lu \n",
  317.             apstring[app->state],
  318.             inet_ntoa(app->target),
  319.             read_timer(&app->timer)/1000,
  320.             dur_timer(&app->timer)/1000);
  321.       }
  322.       return 0;
  323.    }
  324.    /*-------------------------------------------------------------------*
  325.    * give usage information                                             *
  326.    *--------------------------------------------------------------------*/
  327.    if(argc <3) {
  328.       tputs("Usage: setping <address> <interval>\n");
  329.       return 0;
  330.    }
  331.    /*-------------------------------------------------------------------*
  332.    * resolve destination                                                *
  333.    *--------------------------------------------------------------------*/
  334.    if((target = resolve(argv[1])) == 0){
  335.       tprintf(Badhost,argv[1]);      /* ain't never heard of*/
  336.       return 1;
  337.    }
  338.    if((timeout = atol(argv[2])) < 60)
  339.       timeout = 60L;                  /* minimum interval is 60 seconds*/
  340.  
  341.    /*-------------------------------------------------------------------*
  342.    * allocate and fill a list entry                                     *
  343.    *--------------------------------------------------------------------*/
  344.    if (Ap)   {                         /* check if target alreay present*/
  345.       for (app = Ap; app; app = app->next)   {
  346.          if (app->target == target)   {
  347.             ap = app;                   /* gotcha!                      */
  348.             break;
  349.          }
  350.       }
  351.    }
  352.    /*-------------------------------------------------------------------*
  353.    * allocate structure if a new entry to be set                        *
  354.    *--------------------------------------------------------------------*/
  355.    if (ap == NULL)   {
  356.       ap = mxallocw(sizeof(Aping));
  357.       if (Ap == 0)
  358.          Ap = ap;
  359.       else   {
  360.          for (app = Ap; app->next; app = app->next) ;
  361.          app->next = ap;
  362.       }
  363.    }
  364.  
  365.    ap->target = target;
  366.    ap->timer.func = (void (*) __ARGS((void *))) _setping;  /* what to call on timeout      */
  367.    ap->timer.arg = ap;                  /* dummy value                  */
  368.    set_timer(&ap->timer,timeout * 1000L);  /* set timer duration           */
  369. #ifdef MDEBUG
  370.    sprintf(ap->timer.tname,"%.7s",argv[1]);
  371. #endif
  372.    /*-------------------------------------------------------------------*
  373.    * just do a one shot ping  and restart the timer                     *
  374.    *--------------------------------------------------------------------*/
  375.    if (ap->proc_run == False)
  376.       _setping(ap);
  377.  
  378.    return 0;
  379. }
  380.  
  381.  
  382. /*----------------------------------------------------------------------*
  383. *-----------------------------------------------------------------------*/
  384. int
  385. doresetping(int argc,char *argv[], void *p)
  386. {
  387. Aping *app=0, *ap=0;
  388. int32 target;
  389.  
  390.    if((target = resolve(argv[1])) == 0){
  391.       tprintf(Badhost,argv[1]);      /* ain't never heard of*/
  392.       return 1;
  393.    }
  394.  
  395.    /*-------------------------------------------------------------------*
  396.    * isolate the entry                                                  *
  397.    * app == entry to be removed                                         *
  398.    * ap  == previous entry to be connected                              *
  399.    *--------------------------------------------------------------------*/
  400.    for (app = Ap; app; app = app->next)   {
  401.       if (app->target == target)   {
  402.          break;
  403.       }
  404.       ap = app;
  405.    }
  406.  
  407.    if (app == 0)   {                    /* not found                    */
  408.       tprintf(Badhost,argv[1]);
  409.       return 1;
  410.    }
  411.  
  412.    if (app->timer.state == TIMER_STOP)   {
  413.       tprintf("Autoping for %s active, try later...\n",argv[1]);
  414.       return 1;
  415.    }
  416.    /*-------------------------------------------------------------------*
  417.    * disconnect the entry                                               *
  418.    *--------------------------------------------------------------------*/
  419.    if (app == Ap) {                     /* start of chain               */
  420.       Ap = app->next;
  421.    } else {
  422.       ap->next = app->next;
  423.    }
  424.    stop_timer(&app->timer);
  425.    xfree(app);                          /* terminate the entry          */
  426.    return 0;
  427. }
  428.  
  429. static void autoping __ARGS((int a,void *p,void *v));
  430.  
  431.  
  432. /*----------------------------------------------------------------------*
  433. * this routine is called on each timeout                                *
  434. *-----------------------------------------------------------------------*/
  435. static void
  436. _setping(void *x)
  437. {
  438. Aping *ap = x;
  439. static char name[20];
  440.  
  441.    sprintf(name,"AP %.15s",inet_ntoa(ap->target));
  442.    /*-------------------------------------------------------------------*
  443.    * spawn an Autoping process                                          *
  444.    *--------------------------------------------------------------------*/
  445.    ap->proc_run = True;
  446.    newproc(name,768,autoping,ap->state,x,NULL,0);
  447. }
  448.  
  449. /*----------------------------------------------------------------------*
  450. * process to be started for each Autoping                               *
  451. * sorry, but that goto end thing is needed, cuz I have to reset the     *
  452. * 'proc_run" flag upon exit.. and won't leave that up to the optimizer. *
  453. * DK5DC                                                                 *
  454. *-----------------------------------------------------------------------*/
  455. static void
  456. autoping (int oldstate,void *p,void *v)
  457. {
  458. Aping *ap = p;
  459. int s, fromlen;
  460. struct sockaddr_in from;
  461. struct mbuf *bp;
  462. struct icmp icmp;
  463.  
  464.    /*-------------------------------------------------------------------*
  465.    * get a socket
  466.    *--------------------------------------------------------------------*/
  467.    if((s = socket(AF_INET,SOCK_RAW,IPPROTO_ICMP)) == -1){
  468.       tputs(Nosocket);
  469.       goto end;
  470.    }
  471.    stop_timer(&ap->timer);              /* stop the timer               */
  472.    fromlen = sizeof(from);
  473.    pingem(s,ap->target,0,s,64);         /* fire up that ping            */
  474.    /*-------------------------------------------------------------------*
  475.    * now wait and collect replies                                       *
  476.    *--------------------------------------------------------------------*/
  477.    alarm(60000L);         /* Let each ping timeout after 60 seconds*/
  478.  
  479.    for(;;) {
  480.       if(recv_mbuf(s,&bp,0,(char *)&from,&fromlen) == -1){
  481.          if(errno == EALARM)            /* We timed out                 */
  482.             break;
  483.          alarm(0);
  484.          ap->state = oldstate;
  485.          goto end;
  486.       }
  487.  
  488.       ntohicmp(&icmp,&bp);
  489.       free_p(bp);
  490.  
  491.       if(icmp.type != ICMP_ECHO_REPLY       ||
  492.          from.sin_addr.s_addr != ap->target ||
  493.          icmp.args.echo.id != s)
  494.          /* Ignore other people's responses */
  495.          continue;
  496.       alarm(0);
  497.       ap->state = Good;                 /* Finally change state         */
  498.       goto end;
  499.    }
  500.    if (ap->state == Good) {
  501.       ap->state = Suspect;
  502.    } else {
  503.       ap->state = Bad;
  504.    }
  505.  
  506. end:
  507.    close_s(s);
  508.    start_timer(&ap->timer);
  509.    ap->proc_run = False;
  510. }
  511.  
  512.